package com.code.common.util;

import lombok.NoArgsConstructor;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;

/**
 * BigDecimal计算工具类
 * 注意：BigDecimal支持任意精度，任意长度的浮点数运算，但在运算的时候最好设置各个操作数的小数精确度，
 * 特别是除法。结果需要保留几位小数，如果没有设置除法的操作数的小数精确度，计算结果的精确度就会和操作数中最低精确度一致，
 * 导致计算结果不正确。比如：
 * String a = "1";
 * String b = "4.56";
 * BigDecimal aBD = new BigDecimal(a);
 * BigDecimal bBD = new BigDecimal(b);
 * BigDecimal resultBD = aBD.divide(bBD).setScale(3, java.math.BigDecimal.ROUND_HALF_UP);
 * 结果为：0，不是你期望的0.219，如果期望是0.219，则：
 * String a = "1";
 * String b = "4.56";
 * BigDecimal aBD = new BigDecimal(a).setScale(3);
 * BigDecimal bBD = new BigDecimal(b).setScale(3);
 * BigDecimal resultBD = aBD.divide(bBD).setScale(3, java.math.BigDecimal.ROUND_HALF_UP);
 * 还有一个需要注意的点，计算结果，如例子中的resultBD一定要设置其setScale的第二个参数，不然会报错。如果计算结果没有按某个方式进行截断，那么机器就不知道如何去取这个结果了，因此报错。
 *
 * @author tangyifei
 */
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class BigDecimalUtil {

    /**
     * 传的对象不存在则默认值为0
     *
     * @param obj BigDecimal对象
     * @return BigDecimal对象
     */
    public static BigDecimal defaultZero(Object obj) {
        if (ObjectUtils.isEmpty(obj)) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(String.valueOf(obj));
    }

    /**
     * 计算两个字符串相加
     *
     * @param s1 字符串s1
     * @param s2 字符串s2
     * @return 两个字符串相加后的结果
     */
    public static BigDecimal add(String s1, String s2) {
        return defaultZero(s1).add(defaultZero(s2));
    }

    /**
     * 计算字符串与bigDecimal类型相加
     *
     * @param bigDecimal bigDecimal类型参数
     * @param s2         字符串类型参数
     * @return 相加的结果
     */
    public static BigDecimal add(BigDecimal bigDecimal, String s2) {
        BigDecimal b1 = null == bigDecimal ? BigDecimal.ZERO : bigDecimal;
        BigDecimal b2 = defaultZero(s2);
        return b1.add(b2);
    }

    /**
     * 计算两个bigDecimal类型相加
     *
     * @param bigDecimal1 bigDecimal类型参数1
     * @param bigDecimal2 bigDecimal类型参数2
     * @return 相加的结果
     */
    public static BigDecimal add(BigDecimal bigDecimal1, BigDecimal bigDecimal2) {
        BigDecimal b1 = null == bigDecimal1 ? BigDecimal.ZERO : bigDecimal1;
        BigDecimal b2 = null == bigDecimal2 ? BigDecimal.ZERO : bigDecimal2;
        return b1.add(b2);
    }

    /**
     * 计算两个数相减
     *
     * @param s1 字符串1
     * @param s2 字符串2
     * @return 两个数相减结果
     */
    public static BigDecimal subtract(String s1, String s2) {
        return defaultZero(s1).subtract(defaultZero(s2));
    }

    /**
     * 计算两个数相减
     *
     * @param bigDecimal1 bigDecimal类型参数1（被减数）
     * @param bigDecimal2 bigDecimal类型参数2（减数）
     * @return 两个数相减结果
     */
    public static BigDecimal subtract(BigDecimal bigDecimal1, BigDecimal bigDecimal2) {
        BigDecimal b1 = null == bigDecimal1 ? BigDecimal.ZERO : bigDecimal1;
        BigDecimal b2 = null == bigDecimal2 ? BigDecimal.ZERO : bigDecimal2;
        return b1.subtract(b2);
    }

    /**
     * 计算两个数相乘
     *
     * @param s1 字符串1
     * @param s2 字符串2
     * @return 两个数相乘的结果
     */
    public static BigDecimal multiply(String s1, String s2) {
        return defaultZero(s1).multiply(defaultZero(s2));
    }

    /**
     * 计算两个数相乘
     *
     * @param bigDecimal1 bigDecimal类型参数1
     * @param bigDecimal2 bigDecimal类型参数2.
     * @return 两个数相乘的结果
     */
    public static BigDecimal multiply(BigDecimal bigDecimal1, BigDecimal bigDecimal2) {
        BigDecimal b1 = null == bigDecimal1 ? BigDecimal.ZERO : bigDecimal1;
        BigDecimal b2 = null == bigDecimal2 ? BigDecimal.ZERO : bigDecimal2;
        return b1.multiply(b2);
    }

    /**
     * 计算两个数相除
     *
     * @param s1 字符串1
     * @param s2 字符串2
     * @return 两个数相除的结果
     */
    public static BigDecimal divide(String s1, String s2) {
        return defaultZero(s1).divide(defaultZero(s2), 2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 计算两个数相除
     *
     * @param bigDecimal1 bigDecimal类型参数1 （被除数）
     * @param bigDecimal2 bigDecimal类型参数2 （除数）
     * @return 两个数相除的结果
     */
    public static BigDecimal divide(BigDecimal bigDecimal1, BigDecimal bigDecimal2) {
        BigDecimal b1 = null == bigDecimal1 ? BigDecimal.ZERO : bigDecimal1;
        BigDecimal b2 = null == bigDecimal2 ? BigDecimal.ONE : bigDecimal2;
        return b1.divide(b2);
    }

    /**
     * 保留4位小数
     *
     * @param s     字符串金额
     * @param scale 保留的小数位数
     * @return 保留4位小数的BigDecimal
     */
    public static BigDecimal keepDecimalPlaces(BigDecimal s, int scale) {
        if (null == s) {
            return BigDecimal.ZERO;
        }
        return s.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 对double数字进行精度处理，并将精度后面的数据进行四舍五入
     *
     * @param v
     * @param scale
     * @return
     */
    public static String round(BigDecimal v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal one = new BigDecimal("1");
        return (v.divide(one, scale, BigDecimal.ROUND_HALF_UP)).toString();
    }

    /**
     * 格式化数据-去除后边的0
     *
     * @param v1
     * @return
     */
    public static String formatDecimalStr(String v1) {
        BigDecimal b1 = new BigDecimal(v1);
        return b1.stripTrailingZeros().toPlainString();
    }

    /**
     * 从三个数中取出最大数
     *
     * @param rd 数值1
     * @param sl 数值2
     * @param sj 数值3
     * @return 最大数
     */
    public static String getMaxValue(BigDecimal rd, BigDecimal sl, BigDecimal sj) {
        BigDecimal[] array = {rd, sl, sj};
        BigDecimal max = array[0];
        //获取最大值
        for (int i = 0; i < array.length; i++) {
            if (array[i].compareTo(max) == 1) {
                max = array[i];
            }
        }
        return max.toPlainString();
    }

    /**
     * 判断字符串是否为BigDecimal类型
     *
     * @param str 字符串
     * @return 是否为BigDecimal类型
     */
    public static boolean isBigDecimal(String str) {
        if (str == null || str.trim().length() == 0) {
            return false;
        }
        char[] chars = str.toCharArray();
        int sz = chars.length;
        int i = (chars[0] == '-') ? 1 : 0;
        if (i == sz) {
            return false;
        }

        if (chars[i] == '.') {
            //除了负号，第一位不能为'小数点'
            return false;
        }

        boolean radixPoint = false;
        for (; i < sz; i++) {
            if (chars[i] == '.') {
                if (radixPoint) {
                    return false;
                }
                radixPoint = true;
            } else if (!(chars[i] >= '0' && chars[i] <= '9')) {
                return false;
            }
        }
        return true;

    }

}
